package com.mms.entity.dao.impl;

import com.mms.entity.dao.HibernateDao;
import com.mms.entity.model.Page;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.*;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.Query;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Created by dan3652@sina.com on 2016-12-07 17:18.
 */
public class HibernateDaoImpl extends HibernateDaoSupport implements HibernateDao {
    private String pojoName;
    private Class pojoClass;

    public Class getPojoClass() {
        return pojoClass;
    }

    @Override
    public Session getCurrentSession() {
        return super.currentSession();
    }

    @Override
    public SessionFactory getHibernateSessionFactory() {
        return super.getSessionFactory();
    }

    public void setPojoClass(Class pojoClass) {
        this.pojoName = pojoClass.getCanonicalName();
        this.pojoClass = pojoClass;
    }

    public void flush() {
        this.currentSession().flush();
    }
    public void evict(Object obj) {
        this.currentSession().evict(obj);
    }


    @Override
    public Object get(Serializable id) {
        return this.currentSession().get(this.pojoClass, id);
    }

    @Override
    public void saveOrUpdate(Object obj) {
        Object var = this.currentSession().merge(obj);
        System.out.println(var);
        this.currentSession().update(var);
    }

    @Override
    public Serializable save(Object obj) {
        return this.currentSession().save(obj);
    }

    @Override
    public List getAll() {
        return this.find("from "+this.pojoName,new Object[0]);
    }

    @Override
    public List find(String hql, Object... args) {
        Query query = this.currentSession().createQuery(hql);

        for(int i = 0; i < args.length; ++i) {
            query.setParameter(i, args[i]);
        }

        query.setCacheable(true);
        return query.list();
    }

    @Override
    public List findByIn(Integer... strs) {
        return this.currentSession().byMultipleIds(pojoClass).enableSessionCheck(true).multiLoad(strs);
    }

    @Override
    public List find(int start, int end, String hql, Object... args) {
        Query query = this.currentSession().createQuery(hql);

        for(int i = 0; i < args.length; ++i) {
            query.setParameter(i, args[i]);
        }

        query.setCacheable(true);
        query.setFirstResult(start);
        query.setMaxResults(end - start);
        return query.list();
    }

    @Override
    public Page pagedQuery(Page page) {
        String hql = "FROM " + this.pojoName + " obj";
        return this.getPage(hql, page, new Object[0]);
    }

    @Override
    public Page pagedQuery(String fieldName, Object obj, Page page) {
        String hql = "FROM " + this.pojoName + " obj WHERE obj." + fieldName + "=?";
        return this.getPage(hql, page, new Object[]{obj});
    }

    @Override
    public Page pagedQuery(Map<String, Object> map, Page page) {
        String hql = "FROM " + this.pojoName + " obj WHERE 1=1";
        Object[] objs = new Object[map.size()];
        int i = 0;

        Object obj;
        for(Iterator iterator = map.keySet().iterator(); iterator.hasNext(); objs[i++] = obj) {
            String key = (String) iterator.next();
            obj = map.get(key);
            hql = hql + "  and obj." + key + "=?";
        }

        return this.getPage(hql, page, objs);
    }

    @Override
    public Page pagedQuery(String hql, Page page, Object... objs) {
        return this.getPage(hql, page, objs);
    }

    @Override
    public Page pagedQuery(Page pager, DetachedCriteria detachedCriteria) {
        if(pager == null) {
            pager = new Page();
        }

        int pageNumber = pager.getPageNo().intValue();
        int pageSize = pager.getPageSize().intValue();
        String orderBy = pager.getOrderBy();
        Page.OrderType orderType = pager.getOrderType();
        Criteria criteria = detachedCriteria.getExecutableCriteria(this.currentSession());
        long totalCount = ((long)criteria.setProjection(Projections.rowCount()).uniqueResult());
        long pageCount = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
        criteria.setProjection(null);
        criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
        criteria.setFirstResult((pageNumber - 1) * pageSize);
        criteria.setMaxResults(pageSize);
        String identifierPropertyName = null;
        if(StringUtils.isNotEmpty(orderBy) && orderType != null) {
            String metadata = "";
            if(orderBy.contains(".")) {
                identifierPropertyName = StringUtils.substringBefore(orderBy, ".");
                String propertySuffix = StringUtils.substringAfter(orderBy, ".");
                criteria.createAlias(identifierPropertyName, "model");
                metadata = "model." + propertySuffix;
            } else {
                metadata = orderBy;
            }

            if(orderType == Page.OrderType.asc) {
                criteria.addOrder(Order.asc(metadata));
            } else {
                criteria.addOrder(Order.desc(metadata));
            }
        }

        if(StringUtils.isNotEmpty(identifierPropertyName))  criteria.addOrder(Order.desc(identifierPropertyName));
        pager.setPageCount(pageCount);
        pager.setTotalCount(totalCount);
        pager.setList(criteria.list());
        return pager;
    }

    @Override
    public Page pageQueryBySQL(String sql, Page page) {
        int pageNo = page.getPageNo().intValue() < 1 ? 1 : page.getPageNo().intValue();
        StringBuffer countSQL = new StringBuffer("SELECT COUNT(*) FROM (");
        countSQL.append(sql);
        countSQL.append(") a");
        Long total = (Long) this.currentSession().createNativeQuery(countSQL.toString()).uniqueResult();
        long totalPage = total.longValue() % page.getPageSize().intValue() == 0 ? total.longValue() / page.getPageSize().intValue():total.longValue() / page.getPageSize().intValue() + 1;
        totalPage = totalPage < 1 ? 1 : totalPage;
        if(total.longValue() > 0) {
            NativeQuery query = this.currentSession().createNativeQuery(sql);
            query.setFirstResult((pageNo - 1) * page.getPageSize().intValue());
            query.setMaxResults(page.getPageSize().intValue());
            List result = query.list();
            page.setList(result);
        }

        page.setPageCount(totalPage);
        page.setTotalCount(total.longValue());
        return page;
    }

    @Override
    public Page pageQueryBySQL(String sql, Page page, Object... args) {
        int pageNo = page.getPageNo().intValue() < 1 ? 1: page.getPageNo().intValue();
        StringBuffer countSQL = new StringBuffer("SELECT COUNT(*) FROM (");
        countSQL.append(sql);
        countSQL.append(") a");
        NativeQuery countQuery = this.currentSession().createNativeQuery(countSQL.toString());

        for(int total = 0; total < args.length; ++total) {
            countQuery.setParameter(total, args[total]);
        }

        Long count = (long)countQuery.list().size();
        long totalPage = count.longValue() % page.getPageSize().intValue() == 0 ? count.longValue() / page.getPageSize().intValue(): count.longValue() / page.getPageSize().intValue() + 1;
        totalPage = totalPage < 1 ? 1 : totalPage;
        if( count.longValue() > 0 ) {
            NativeQuery query = this.currentSession().createNativeQuery(sql);
            query.setFirstResult((pageNo - 1) * page.getPageSize().intValue());
            query.setMaxResults(page.getPageSize().intValue());

            for(int result = 0; result < args.length; ++result) {
                query.setParameter(result, args[result]);
            }

            List list = query.list();
            page.setList(list);
        }

        page.setPageCount(totalPage);
        page.setTotalCount(count.longValue());
        return page;
    }
    private Page getPage(String hql, Page page, Object... objs) {
        HibernateTemplate hibernateTemplate = this.getHibernateTemplate();
        String[] sections = hql.split("\\s");
        String first = sections[0].toUpperCase();
        String countHql = null;
        if("FROM".equals(first)) {
            countHql = "SELECT COUNT(*) " + hql;
        } else {
            countHql = "SELECT COUNT(*) FROM(" + hql + ")";
        }

        List ints = hibernateTemplate.find(countHql, objs);
        int pageNo = page.getPageNo().intValue();
        int pageSize = page.getPageSize().intValue();
        int totalCount = ((Long)ints.get(0)).intValue();
        int pageCount = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
        Query query = this.currentSession().createQuery(hql);

        for(int pojoTypes = 0; pojoTypes < objs.length; ++pojoTypes) {
            query.setParameter(pojoTypes, objs[pojoTypes]);
        }

        query.setFirstResult((pageNo - 1) * pageSize);
        query.setMaxResults(pageSize);
        List list = query.list();
        page.setPageCount(pageCount);
        page.setList(list);
        page.setTotalCount(totalCount);
        return page;
    }

       public Long getCountBy(String fieldName, Object obj) {
        String hql = "from " + this.pojoName + " obj where obj." + fieldName + "=?";
       Query query = this.getCurrentSession().createQuery(hql);
        query.setParameter(0, obj);
        return (Long) query.uniqueResult();
    }

    public Long getCount(String hql, Object... args) {
        hql = "SELECT COUNT(*) " + hql.substring(hql.toUpperCase().indexOf("FROM"));
        Query query = this.getCurrentSession().createQuery(hql);

        for(int i = 0; i < args.length; ++i) {
            query.setParameter(i, args[i]);
        }
        query.setCacheable(true);
        return (Long) query.uniqueResult();
    }

    public Long getCount(String hql) {
        hql = "SELECT COUNT(*) " + hql.substring(hql.toUpperCase().indexOf("FROM"));
        Query query = this.getCurrentSession().createQuery(hql);
        query.setCacheable(true);
        return (Long) query.uniqueResult();
    }

    public Long getCount(DetachedCriteria detachedCriteria) {
        Criteria criteria = detachedCriteria.getExecutableCriteria(this.getCurrentSession());
        criteria.setCacheable(true);
        int totalCount = ((Integer)criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
        return new Long(totalCount);
    }

    public Long getCount(Map<String, Object> map) {
        String hql = "FROM " + this.pojoName + " obj where 1=1";
        Object[] objs = new Object[map.size()];
        int i = 0;

        String key;
        for(Iterator var6 = map.keySet().iterator(); var6.hasNext(); objs[i++] = map.get(key)) {
            key = (String)var6.next();
            hql = hql + "  and obj." + key + "=?";
        }

        return this.getCount(hql, objs);
    }

    public int bulkUpdate(String hql) {
        return this.getHibernateTemplate().bulkUpdate(hql);
    }

    public int bulkUpdate(String hql, Object obj) {
        return this.getHibernateTemplate().bulkUpdate(hql, obj);
    }

    public int bulkUpdate(String hql, Object... args) {
        return this.getHibernateTemplate().bulkUpdate(hql, args);
    }

    public SQLQuery createSQLQuery(String sql) {
        return this.currentSession().createSQLQuery(sql);
    }

    public Query createQuery(String hql) {
        return this.currentSession().createQuery(hql);
    }

    public Object getObject(DetachedCriteria detachedCriteria) {
        Criteria criteria = detachedCriteria.getExecutableCriteria(this.currentSession());
        criteria.setCacheable(true);
        return criteria.uniqueResult();
    }

    public Object getObject(String hql, Object... args) {
        Query query = this.currentSession().createQuery(hql);

        for(int i = 0; i < args.length; ++i) {
            query.setParameter(i, args[i]);
        }

        query.setCacheable(true);
        return query.uniqueResult();
    }

    public Object getObjectBy(String fieldName, Object obj) {
        String hql = "from " + this.pojoName + " obj where obj." + fieldName + "=?";
        Query query = this.currentSession().createQuery(hql);
        query.setParameter(0, obj);
        return query.uniqueResult();
    }

    public Object getObjectBy(Map<String, Object> map) {
        String hql = "from " + this.pojoName + " obj where 1=1";
        Object[] objs = new Object[map.size()];
        int i = 0;

        String key;
        for(Iterator var6 = map.keySet().iterator(); var6.hasNext(); objs[i++] = map.get(key)) {
            key = (String)var6.next();
            hql = hql + " and obj." + key + "=?";
        }

        return this.getObject(hql, objs);
    }
}
